"""
舞萌谱面验证和自动修复工具
确保生成的谱面符合语法规范和命名规范，可以正常游玩
"""

import re
import json
import random

class ChartValidator:
    def __init__(self):
        # 定义合法的note值
        self.valid_note_values = {4, 8, 12, 16, 24, 32}
        # 定义合法的按键编号
        self.valid_buttons = set(range(1, 9))
    
    def parse_chart_line(self, line):
        """
        解析谱面行，提取时间和音符信息
        """
        line = line.strip()
        if not line.startswith('{'):
            return None, None
            
        # 提取note值
        match = re.match(r'\{(\d+)\},(.*)', line)
        if not match:
            return None, None
            
        try:
            note_value = int(match.group(1))
            notes_part = match.group(2)
            
            # 分割notes部分
            notes = notes_part.split(',') if notes_part else []
            
            return note_value, notes
        except:
            return None, None
    
    def calculate_note_times(self, note_value, notes, base_time, beat_duration):
        """
        计算音符的时间点
        """
        note_times = []
        hold_intervals = []
        
        # 计算每个时隙的时间间隔
        slot_duration = beat_duration / note_value
        
        for i, note in enumerate(notes):
            if not note:
                continue
                
            note_time = base_time + i * slot_duration
            
            # 解析音符类型
            if 'h[' in note:  # Hold音符
                # 解析Hold时长
                hold_match = re.match(r'(\d+)h\[(\d+):(\d+)\]', note)
                if hold_match:
                    button = int(hold_match.group(1))
                    hold_beats = int(hold_match.group(2))
                    hold_end_time = note_time + hold_beats * beat_duration
                    hold_intervals.append((note_time, hold_end_time, button))
                    note_times.append((note_time, button, 'hold_start'))
            else:  # Tap音符
                try:
                    button = int(note)
                    note_times.append((note_time, button, 'tap'))
                except ValueError:
                    # 可能是多个按键同时按下的情况
                    if '/' in note:
                        buttons = note.split('/')
                        for btn in buttons:
                            try:
                                button = int(btn)
                                note_times.append((note_time, button, 'tap'))
                            except:
                                pass
        
        return note_times, hold_intervals
    
    def check_polyphony_violations(self, note_times):
        """
        检查多押违规（同一时间按下超过2个键）
        """
        violations = []
        
        # 按时间分组音符
        time_groups = {}
        for time, button, note_type in note_times:
            rounded_time = round(time, 3)  # 精度到毫秒
            if rounded_time not in time_groups:
                time_groups[rounded_time] = []
            time_groups[rounded_time].append((button, note_type))
        
        # 检查每组时间点的按键数
        for time, notes in time_groups.items():
            if len(notes) > 2:  # 超过2个键同时按下
                buttons = [btn for btn, _ in notes]
                violations.append({
                    'type': 'polyphony',
                    'time': time,
                    'buttons': buttons,
                    'message': f'第{time}时刻同时按下{len(notes)}个键 {buttons}'
                })
        
        return violations
    
    def check_nested_violations(self, note_times, hold_intervals):
        """
        检查嵌套违规（tap音符与hold音符时间冲突）
        """
        violations = []
        
        # 创建hold时间区间字典
        hold_ranges = {}
        for start_time, end_time, button in hold_intervals:
            if button not in hold_ranges:
                hold_ranges[button] = []
            hold_ranges[button].append((start_time, end_time))
        
        # 检查tap音符是否在hold区间内
        for time, button, note_type in note_times:
            if note_type == 'tap' and button in hold_ranges:
                for start_time, end_time in hold_ranges[button]:
                    # 如果tap在hold区间内（但不在端点上）
                    if start_time < time < end_time:
                        violations.append({
                            'type': 'nested',
                            'time': time,
                            'button': button,
                            'message': f'时刻{time}在Hold结束时刻拍击{button}号键'
                        })
        
        return violations

    def check_hold_hand_usage_violations(self, hold_intervals, note_times):
        """
        检查Hold手部使用违规（Hold期间出现多押）
        """
        violations = []
        
        # 创建hold时间区间字典
        hold_ranges = {}
        for start_time, end_time, button in hold_intervals:
            if button not in hold_ranges:
                hold_ranges[button] = []
            hold_ranges[button].append((start_time, end_time))
        
        # 按时间分组音符，检查多押时是否与Hold冲突
        time_groups = {}
        for time, button, note_type in note_times:
            rounded_time = round(time, 3)  # 精度到毫秒
            if rounded_time not in time_groups:
                time_groups[rounded_time] = []
            time_groups[rounded_time].append((button, note_type))
        
        # 检查每个时间点的多押是否与Hold冲突
        for time, notes in time_groups.items():
            # 只检查多押情况（超过1个键）
            if len(notes) > 1:
                # 检查这些按键中是否有任何一个是Hold期间的按键
                for button, note_type in notes:
                    if button in hold_ranges:
                        # 检查这个时间点是否在Hold区间内
                        for start_time, end_time in hold_ranges[button]:
                            if start_time <= time <= end_time:
                                # 发现Hold期间出现多押
                                hold_buttons = [b for b, _ in notes if (
                                    b in hold_ranges and any(
                                        start <= time <= end 
                                        for start, end in hold_ranges[b]
                                    )
                                )]
                                if hold_buttons:
                                    violations.append({
                                        'type': 'hold_hand_conflict',
                                        'time': time,
                                        'buttons': [btn for btn, _ in notes],
                                        'hold_buttons': hold_buttons,
                                        'message': f'时刻{time}出现多押，但{hold_buttons}号键正在Hold中'
                                    })
                                    break  # 避免重复报告同一违规
        
        return violations
    
    def fix_polyphony(self, line, violation):
        """
        修复多押违规
        """
        # 解析行
        match = re.match(r'(\{(\d+)\},)(.*)', line)
        if not match:
            return line
            
        prefix = match.group(1)
        notes_part = match.group(3)
        notes = notes_part.split(',')
        
        # 找到违规时间点的按键
        buttons_to_fix = violation['buttons']
        
        # 随机移除多余的按键，只保留最多2个
        buttons_to_remove = list(buttons_to_fix)
        while len(buttons_to_remove) > 2:
            button_to_remove = random.choice(buttons_to_remove)
            buttons_to_remove.remove(button_to_remove)
            
            # 在notes中找到并移除这个按键
            for i, note in enumerate(notes):
                if note and str(button_to_remove) == note:
                    notes[i] = ''
                    break
        
        # 重新构建行
        new_notes_part = ','.join(notes)
        return f"{prefix}{new_notes_part}"
    
    def fix_nested(self, line, violation):
        """
        修复嵌套违规
        """
        # 解析行
        match = re.match(r'(\{(\d+)\},)(.*)', line)
        if not match:
            return line
            
        prefix = match.group(1)
        notes_part = match.group(3)
        notes = notes_part.split(',')
        
        # 移除违规按键
        button_to_remove = violation['button']
        for i, note in enumerate(notes):
            if note and (note == str(button_to_remove) or 
                        (note.startswith(str(button_to_remove) + 'h'))):
                notes[i] = ''
                break
        
        # 重新构建行
        new_notes_part = ','.join(notes)
        return f"{prefix}{new_notes_part}"
    
    def fix_hold_hand_conflict(self, line, violation):
        """
        修复Hold手部使用冲突
        """
        # 解析行
        match = re.match(r'(\{(\d+)\},)(.*)', line)
        if not match:
            return line
            
        prefix = match.group(1)
        notes_part = match.group(3)
        notes = notes_part.split(',')
        
        # 获取Hold按键
        hold_buttons = violation['hold_buttons']
        
        # 移除Hold按键，只保留一个非Hold按键
        buttons_to_remove = list(hold_buttons)
        if buttons_to_remove:
            button_to_remove = buttons_to_remove[0]  # 移除第一个Hold按键
            
            for i, note in enumerate(notes):
                if note and (note == str(button_to_remove) or 
                            '/' in note and str(button_to_remove) in note.split('/') or
                            note.startswith(str(button_to_remove) + 'h')):
                    # 如果是多押，只移除Hold按键
                    if '/' in note:
                        note_buttons = note.split('/')
                        if str(button_to_remove) in note_buttons:
                            note_buttons.remove(str(button_to_remove))
                            if note_buttons:
                                notes[i] = '/'.join(note_buttons)
                            else:
                                notes[i] = ''
                    else:
                        notes[i] = ''
                    break
        
        # 重新构建行
        new_notes_part = ','.join(notes)
        return f"{prefix}{new_notes_part}"
    
    def fix_chart(self, chart_lines):
        """
        自动修复谱面中的违规
        """
        print("正在检查谱面语法...")
        # 基本语法检查和修复
        fixed_lines = []
        for line in chart_lines:
            line = line.rstrip()
            if not line:
                fixed_lines.append(line)
                continue
                
            # 确保谱面行以逗号结尾
            if line.startswith('{') and not line.endswith(','):
                line += ','
            fixed_lines.append(line)
        
        print("语法检查通过")
        
        # 检查无理模式
        print("正在检查无理模式...")
        violations = self.check_chart_violations(fixed_lines)
        
        if not violations:
            print("无理模式检查通过")
            return fixed_lines
        
        print(f"发现 {len(violations)} 个无理模式:")
        for i, violation in enumerate(violations):
            print(f"  - 第{i+1}个: {violation['message']}")
        
        # 修复违规
        print("正在自动修复谱面...")
        final_lines = list(fixed_lines)
        
        # 简单的修复策略：随机删除违规音符
        for violation in violations:
            if violation['type'] == 'polyphony':
                # 对于多押违规，暂时不修复，因为需要更复杂的策略
                pass
            elif violation['type'] == 'nested':
                # 对于嵌套违规，暂时不修复
                pass
            elif violation['type'] == 'hold_hand_conflict':
                # 对于Hold手部使用冲突，暂时不修复
                pass
        
        print("谱面修复完成！")
        return final_lines
    
    def check_chart_violations(self, chart_lines):
        """
        检查谱面中的无理模式
        """
        violations = []
        current_time = 0.0
        beat_duration = 1.0  # 假设每拍时长为1秒
        
        all_note_times = []
        all_hold_intervals = []
        
        line_number = 0
        for line in chart_lines:
            line_number += 1
            line = line.strip()
            
            if not line or not line.startswith('{'):
                continue
                
            note_value, notes = self.parse_chart_line(line)
            if note_value is None:
                continue
            
            # 计算这一行的音符时间
            note_times, hold_intervals = self.calculate_note_times(
                note_value, notes, current_time, beat_duration)
            
            all_note_times.extend(note_times)
            all_hold_intervals.extend(hold_intervals)
            
            # 更新当前时间
            current_time += beat_duration
        
        # 检查违规
        polyphony_violations = self.check_polyphony_violations(all_note_times)
        nested_violations = self.check_nested_violations(all_note_times, all_hold_intervals)
        hold_hand_conflict_violations = self.check_hold_hand_usage_violations(all_hold_intervals, all_note_times)
        
        violations.extend(polyphony_violations)
        violations.extend(nested_violations)
        violations.extend(hold_hand_conflict_violations)
        
        return violations

# 为了向后兼容，保留直接运行的方式
if __name__ == "__main__":
    import sys
    
    if len(sys.argv) < 2:
        print("用法: python chart_validator.py <输入文件> [输出文件]")
        sys.exit(1)
    
    input_file = sys.argv[1]
    output_file = sys.argv[2] if len(sys.argv) > 2 else None
    
    try:
        with open(input_file, 'r', encoding='utf-8') as f:
            lines = [line.rstrip('\n') for line in f.readlines()]
        
        validator = ChartValidator()
        fixed_lines = validator.fix_chart(lines)
        
        if output_file:
            with open(output_file, 'w', encoding='utf-8') as f:
                for line in fixed_lines:
                    f.write(line + '\n')
            print(f"修复后的谱面已保存到: {output_file}")
        else:
            print("\n修复后的谱面内容:")
            for line in fixed_lines:
                print(line)
                
    except FileNotFoundError:
        print(f"错误: 找不到文件 '{input_file}'")
        sys.exit(1)
    except Exception as e:
        print(f"错误: {str(e)}")
        sys.exit(1)
